package org.rkuk.kasutil.test.system 
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.getTimer;
	import org.rkuk.kasutil.test.event.TestMethodEvent;
	import org.rkuk.kasutil.test.framework.TestCase;
	import org.rkuk.kasutil.test.framework.TestMethod;
	import org.rkuk.kasutil.test.system.stack.HandlerStack;
	import org.rkuk.kasutil.test.system.stack.OrdinalHandlerStack;
	
	/**
	 * TestThread is used for running TestCase
	 * including control the flow and record the executation process
	 * @author rkuk
	 */
	public class TestThread extends EventDispatcher 
	{
		private var testCase_:TestCase;
		private var handlerStack_:HandlerStack;
		private var testContext_:TestContext;
		private var executionTime_:int;
		
		public function TestThread() 
		{
			
		}
		
		public function runTestCase(testCase:TestCase):void
		{
			testCase_ = testCase;
			handlerStack_ = new OrdinalHandlerStack(this);
			testContext_ = new TestContext(testCase_, handlerStack_);
			
			threadStart();
		}
		
		private function dispose():void
		{
			testCase_.destroyTestObject();
			handlerStack_.dispose();
			
			testCase_ = null;
			handlerStack_ = null;
			testContext_ = null;
		}
		
		private function threadStart():void 
		{
			executionTime_ = getTimer();
			testCaseSetUp();
		}
		
		private function testCaseSetUp():void 
		{
			testCase_.setUp.addEventListener(TestMethodEvent.EXECUTE, testCaseRun);
			runTestMethod(testCase_.setUp);
		}
		
		private function testCaseRun(e:TestMethodEvent):void 
		{
			testCase_.setUp.removeEventListener(TestMethodEvent.EXECUTE, testCaseRun);
			if (e.isSuccess)
			{
				testCase_.testMethod.addEventListener(TestMethodEvent.EXECUTE, testCaseTearDown);
				runTestMethod(testCase_.testMethod);
			}
			else
				threadEnd()
		}
		
		private function testCaseTearDown(e:TestMethodEvent):void 
		{
			testCase_.testMethod.removeEventListener(TestMethodEvent.EXECUTE, testCaseTearDown);
			testCase_.tearDown.addEventListener(TestMethodEvent.EXECUTE, threadEnd);
			runTestMethod(testCase_.tearDown);
		}
		
		private function threadEnd(e:TestMethodEvent=null):void 
		{
			executionTime_ = getTimer() - executionTime_;
			if (testCase_.tearDown.hasEventListener(TestMethodEvent.EXECUTE))
				testCase_.tearDown.removeEventListener(TestMethodEvent.EXECUTE, threadEnd);
				
			dispose();
		}
		
		private function runTestMethod(testMethod:TestMethod, ...params):void
		{
			try
			{
				listenToTestMethodExecution(testMethod);
				
				testMethod.params = params;
				var pureExecutionTime:int = testMethod.execute(testContext_));
				testMethod.params = null;
				
				if (!testMethod.isExecute)//AnsyTestMethod or TestMethodGroup
				{
					//TODO:start timer and report pure time without ansy
					handlerStack_.startHandlerInBuffer();
				}
			}
			catch (e:Error)
			{
				handlerStack_.clearBuffer();
				//TODO:classify the error tpye and generate different message
				testMethod.fail(e.message);
			}
		}
		
		private function listenToTestMethodExecution(testMethod:TestMethod):void 
		{
			
		}
		
		public function registerAnsyHandler(target:EventDispatcher, eventType:String, eventHandler:TestMethod, timeout:int, timeoutHandler:TestMethod):void
		{
			
		}
		
		/**
		 * @private
		 * this method is called by ansyHandlerStack_ when ansyEvent fired or timeout
		 * @param	handler
		 * @param	e
		 */
		public function onStackPop(methodPopped:TestMethod,triggerEvent:Event=null):void
		{
			if (triggerEvent == null)
				runTestMethod(methodPopped);
			else	
				runTestMethod(methodPopped, triggerEvent);
		}
	}
}